package com.alpha.service.utils;

import com.alpha.core.entity.AlphaQuery;
import com.alpha.core.utils.StringUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import lombok.extern.slf4j.Slf4j;

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.function.UnaryOperator;

/**
 * QueryWrapper 构造器
 *
 * @author chenruwan
 * @since 2022-01-01
 */
@Slf4j
public class QueryWrapperBuilder {
    private QueryWrapperBuilder() {
    }

    public static <T> QueryWrapper<T> build(AlphaQuery query) {
        return build(query, null, false);
    }

    public static <T> QueryWrapper<T> build(AlphaQuery query, boolean isMultiTable) {
        return build(query, null, isMultiTable);
    }

    public static <T> QueryWrapper<T> build(AlphaQuery query, List<String> searchableFields) {
        return build(query, searchableFields, false);
    }

    public static <T> QueryWrapper<T> build(AlphaQuery query, List<String> searchableFields, boolean isMultiTable) {
        return build(query, searchableFields, isMultiTable ? QueryWrapperBuilder::addDefaultTableName : null);
    }

    public static <T> QueryWrapper<T> build(AlphaQuery query, List<String> searchableFields,
                                            UnaryOperator<String> mapper) {
        QueryWrapper<T> queryWrapper = new QueryWrapper<>();

        orders(queryWrapper, query.getOrders(), mapper);
        equals(queryWrapper, query.getEquals(), mapper);
        likes(queryWrapper, query.getLikes(), mapper);
        betweens(queryWrapper, query.getBetweens(), mapper);
        ins(queryWrapper, query.getIns(), mapper);
        search(queryWrapper, query.getSearch(), searchableFields, mapper);
        queryParams(queryWrapper, query.getQueryParams(), mapper);

        return queryWrapper;
    }

    private static <T> void search(QueryWrapper<T> wrapper, String search,
                                   List<String> searchableFields, UnaryOperator<String> mapper) {
        if (StringUtil.isNotEmpty(search) && searchableFields != null) {
            wrapper.and(innerWrapper ->
                    searchableFields.forEach(each -> innerWrapper.or().like(mapping(mapper, each), search)));
        }
    }

    /**
     * 填充默认表名
     */
    private static String addDefaultTableName(String field) {
        if (field.contains(StringUtil.DOT)) {
            return field;
        }
        return "t." + field;
    }

    private static String mapping(UnaryOperator<String> mapper, String field) {
        if (mapper == null) {
            return StringUtil.toUnderlineCase(field);
        }
        return mapper.apply(StringUtil.toUnderlineCase(field));
    }

    private static <T> void queryParams(QueryWrapper<T> wrapper, Map<String, List<String>> queryParams,
                                        UnaryOperator<String> mapper) {
        if (queryParams == null) {
            return;
        }
        for (var entry : queryParams.entrySet()) {
            String key = entry.getKey();
            if (key.endsWith(StringUtil.EMPTY_ARRAY)) {
                wrapper.in(mapping(mapper, key.substring(0, key.length() - 2)), entry.getValue().toArray());
            } else if (key.endsWith(StringUtil.TILDE)) {
                wrapper.like(mapping(mapper, key.substring(0, key.length() - 1)), entry.getValue().get(0));
            } else if (key.endsWith(StringUtil.LESS_THAN)) {
                wrapper.le(mapping(mapper, key.substring(0, key.length() - 1)), entry.getValue().get(0));
            } else if (key.endsWith(StringUtil.GREATER_THAN)) {
                wrapper.ge(mapping(mapper, key.substring(0, key.length() - 1)), entry.getValue().get(0));
            } else {
                if (entry.getValue().size() > 1) {
                    wrapper.in(mapping(mapper, key), entry.getValue().toArray());
                } else {
                    wrapper.eq(mapping(mapper, key), entry.getValue().get(0));
                }
            }
        }
    }

    private static <T> void orders(QueryWrapper<T> wrapper, List<String> orders, UnaryOperator<String> mapper) {
        for (String order : orders) {
            if (order.startsWith(StringUtil.DASHED)) {
                wrapper.orderByDesc(mapping(mapper, StringUtil.removeFirstCharIf(order, '-')));
            } else {
                wrapper.orderByAsc(mapping(mapper, order));
            }
        }
    }

    private static <T> void equals(QueryWrapper<T> wrapper, String equals, UnaryOperator<String> mapper) {
        for (String equal : AlphaQuery.parse(equals)) {
            String[] cols = equal.split(StringUtil.COMMA);
            if (cols.length != 2) {
                continue;
            }
            wrapper.eq(mapping(mapper, cols[0]), cols[1]);
        }
    }

    private static <T> void likes(QueryWrapper<T> wrapper, String likes, UnaryOperator<String> mapper) {
        for (String like : AlphaQuery.parse(likes)) {
            String[] cols = like.split(StringUtil.COMMA);
            if (cols.length != 2) {
                continue;
            }
            wrapper.like(mapping(mapper, cols[0]), cols[1]);
        }
    }

    private static <T> void betweens(QueryWrapper<T> wrapper, String betweens, UnaryOperator<String> mapper) {
        for (String between : AlphaQuery.parse(betweens)) {
            String[] cols = between.split(StringUtil.COMMA);
            if (cols.length != 3) {
                continue;
            }
            wrapper.between(mapping(mapper, cols[0]), cols[1], cols[2]);
        }
    }

    private static <T> void ins(QueryWrapper<T> wrapper, String ins, UnaryOperator<String> mapper) {
        for (String in : AlphaQuery.parse(ins)) {
            String[] cols = in.split(StringUtil.COMMA);
            if (cols.length < 2) {
                continue;
            }
            wrapper.in(mapping(mapper, cols[0]), Arrays.copyOfRange(cols, 1, cols.length, Object[].class));
        }
    }
}
